package net.gdface.bean.jdk.descriptor;

import java.beans.IntrospectionException;
import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.Set;

import com.google.common.base.Throwables;
import com.google.common.collect.Sets;

import gu.sql2java.BaseRow;
import gu.sql2java.RowMetaData;
import static com.google.common.base.Preconditions.checkNotNull;
import static net.gdface.utils.CaseSupport.toCamelcase;

/**
 * 实现对{@link gu.sql2java.BaseBean}接口实例的属性封装
 * @author guyadong
 * @since 2.7.0
 */
public class BaseRowPropertyDescriptor extends BaseNoStandardPropertyDescriptor {
    private final Method readMethod;
	private final Method writeMethod;
	private final Class<?> propertyType;
	private final Set<Class<?>> writableTypes;
	/**
	 * @param metaData
	 * @param name
	 * @throws IntrospectionException
	 * @since 2.8.11
	 */
	public BaseRowPropertyDescriptor(RowMetaData metaData, String name)
			throws IntrospectionException {
		super(checkNotNull(name,"name is null"), null, null);
	    Class<?> columnType = checkNotNull(metaData,"metaData is null").columnTypeOf(name);
	    propertyType = columnType;
	    String columnName = metaData.columnNameOf(metaData.columnIDOf(name));
		try {
			readMethod = getClass().getMethod("readMethod", Object.class,String.class);
			writeMethod = getClass().getMethod("writeMethod", Object.class,String.class,Object.class);
			writableTypes = getWritableTypes(metaData.beanType,columnName);
		} catch (Exception e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		}
	}
	public BaseRowPropertyDescriptor(String name,BaseRow bean)
			throws IntrospectionException {
		this(checkNotNull(bean,"bean is null").fetchMetaData(),name);
	}
	@Override
	public Method getReadMethod() {
		return readMethod;
	}

	@Override
	public Method getWriteMethod() {
		return writeMethod;
	}

	@Override
	public Class<?> getPropertyType() {
		return propertyType;
	}
	@Override
	public boolean isWritable(Object object){
		if(null == object) {
			return true;
		}
		for(Class<?> type: writableTypes) {
			if(type.isInstance(object)) {
				return true;
			}
		}
		return false;
	}

    /**
	 * 字段读取方法实现
	 * @param bean 读取目标对象
	 * @param name 字段名
	 * @return 字段值
	 * @since 2.8.10
	 */
	public static Object readMethod(Object bean,String name){
		if(bean instanceof BaseRow){
			return ((BaseRow)bean).getValue(name);
		}
		return null;
	}
	/**
	 * 字段写入方法实现
	 * @param bean 写入目标对象
	 * @param name 字段名
	 * @param value 要写入的值
	 * @since 2.8.10
	 */
	public static void writeMethod(Object bean,String name,Object value){
		if(bean instanceof BaseRow){
			((BaseRow)bean).setValue(name, value);
		}
	}
	private static Set<Class<?>> getWritableTypes(Class<?> clazz, String columnName) {
		LinkedHashSet<Class<?>> types = Sets.newLinkedHashSet();
		String n1 = toCamelcase("set_" + columnName);
		String n2 = toCamelcase("write_" + columnName);
		for(Method method:clazz.getMethods()) {
			if(method.getParameterCount()==1 && (method.getName().equals(n1) || method.getName().equals(n2))) {
				types.add(method.getParameterTypes()[0]);
			}
		}
		return types;
	}
}
